<?php
if(!class_exists('XARDAO', false)){
	require_once(XFRAMEWORK_DIR.'/module/active_record/XARDAO.php');
}
class XARShardDAO extends XARDAO{
	private static $daos = array();
	protected $tableName = null;
	protected $shardId = null;
	protected $dirverPrefix = null;
	protected $strategy = null;
	protected $tables = array();
	
	private function __construct($modelName){		
		//load model
		$this->loader = XClassLoader::loader($modelName);
		if(!is_subclass_of($modelName, 'XARShardRecord')){
			throw new XORMException("class {$modelName} is not a subclass of XARPartitionRecord");
		}
		$this->annotation = XAnnotation::singleton($modelName);
		//load table info
		$this->tableName = $this->annotation()->get('Table.name');
		//load shard info	
		$this->dirverPrefix = $this->annotation()->get('Shard.name');
		if(is_null($this->dirverPrefix)){
			throw new XORMException("class {$modelName} does not have shard name in annotation.");
		}
		$this->shardId = $this->annotation()->get('Shard.shardId');
		if(is_null($this->shardId)){
			throw new XORMException("class {$modelName} does not have shard id in annotation.");
		}
	}
	
	/**
	 * @return XARShardDAO
	 */	
	public static function dao($model){
		if (!isset(self::$daos[$model])) {
			self::$daos[$model] = new self($model);
		}
		return self::$daos[$model];
	}
	
	public function shardId(){
		return $this->shardId;
	}
	
	/**
	 * @return XPartitionStrategy
	 */		
	public function strategy(){
		//lazy load. should not be called if user realize partition logic in record class  
		if(is_null($this->strategy)){
			$this->_loadStrategy();
		}
		return $this->strategy;
	}
	
	protected function _loadStrategy(){
		//load strategy
		$strategyName = $this->annotation()->get('Strategy.name');
		if(is_null($strategyName)){
			throw new XORMException("class {$this->loader->name()} does not have partition strategy definition in annotations.");
		}
		$class = 'X'.x_name_to_java($strategyName).'ShardStrategy';		
		if(!XClassLoader::loader($class)->isSubclassOf('XShardStrategy')){
			throw new XORMException("class {$class} must implement XShardStrategy");
		}
		$this->strategy = XFactory::singleton()->get($class);
	}
	
	public function insert(XModel $model, array $params = array()){
		$this->loadTable($model, $params);
		return parent::insert($model, $params);
	}
	
	public function update(XModel $model, array $params = array()){
		$this->loadTable($model, $params);
		return parent::update($model, $params);
	}

	public function remove(XModel $model, array $params = array()){
		$this->loadTable($model, $params);
		return parent::remove($model, $params);
	}
	
	/**
	 * @return XShardTable
	 */	
	public function loadTable(XModel $model, array $params = array()){
		$shardId = $this->shardId;
		$value = $model->$shardId;
		if(is_null($value)){
			if(!isset($params[$shardId])){				
				throw new XORMException("class {$this->loader->name()} should have a valid shard id.");
			}
			$value = $params[$shardId];
		}
		$this->table = $this->loadTableByShardId($value);		
		return $this->table;
	}
	
	/**
	 * @return XShardTable
	 */	
	public function loadTableByShardId($value){
		$model = XFactory::singleton()->get($this->loader->name());
		$driverName = $model->shard($this->annotation(), $value);
		$this->table = $this->loadTableByShardName($driverName);
		return $this->table;
	}
	
	/**
	 * @return XShardTable
	 */	
	public function loadTableByShardName($driverName){
		$this->driver = XDB::singleton()->driver($driverName);
		if(!isset($this->tables[$driverName])){			
			$this->tables[$driverName] = $this->driver()->loadTable($this->tableName);
			$this->tables[$driverName]->validate();
		}
		$this->table = $this->tables[$driverName];	
		return $this->table;
	}
		
	public function allShards(){		
		$model = XFactory::singleton()->get($this->loader->name());
		return $model->allShards($this->annotation());		
	}

	public function uniqueId(){
		return $this->shardId();
	}
	
	public function defaultForeignKey(){
		return x_name_to_c($this->tableName).'_id';
	}
		
}
?>